package com.jorado.core.utility;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class NetUtils {

    private static final int DEFAULT_TIMEOUT_IN_SECONDS = 5000;

    public static String getIP() {
        String ip = getLocalIp();
        String osName = System.getProperties().getProperty("os.name");
        if (osName != null) {
            osName = osName.toLowerCase();
            if (osName.startsWith("linux")) {
                try {
                    Enumeration<NetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces();
                    while (allNetInterfaces.hasMoreElements()) {
                        NetworkInterface netInterface = allNetInterfaces.nextElement();
                        Enumeration<InetAddress> addresses = netInterface.getInetAddresses();
                        if (netInterface.getName().startsWith("eth")) {
                            while (addresses.hasMoreElements()) {
                                InetAddress netAddr = addresses.nextElement();
                                if (netAddr != null && netAddr instanceof Inet4Address) {
                                    return netAddr.getHostAddress();
                                }
                            }
                        }
                    }
                } catch (SocketException exp) {

                }
            } else {
                return getLocalIp();
            }
        }
        return ip;
    }

    public static String getLocalIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException exp) {

        }
        return null;
    }

    public static List<String> resolveDNS(String domainName) throws UnknownHostException {
        List<String> result = new ArrayList<>();

        InetAddress[] addresses = InetAddress.getAllByName(domainName);
        if (addresses != null) {
            for (InetAddress addr : addresses) {
                result.add(addr.getHostAddress());
            }
        }

        return result;
    }

    /**
     * ping the url, return true if ping ok, false otherwise
     */
    public static boolean ping(String address) {
        try {
            URL urlObj = new URL(address);
            HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();
            connection.setRequestMethod("GET");
            connection.setUseCaches(false);
            connection.setConnectTimeout(DEFAULT_TIMEOUT_IN_SECONDS);
            connection.setReadTimeout(DEFAULT_TIMEOUT_IN_SECONDS);
            int statusCode = connection.getResponseCode();
            cleanUpConnection(connection);
            return (200 <= statusCode && statusCode <= 399);
        } catch (Throwable ignore) {
        }
        return false;
    }

    /**
     * according to https://docs.oracle.com/javase/7/docs/technotes/guides/net/http-keepalive.html, we should clean up the
     * connection by reading the response body so that the connection could be reused.
     */
    public static void cleanUpConnection(HttpURLConnection conn) {
        InputStreamReader isr = null;
        InputStreamReader esr = null;
        try {
            isr = new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8);
            IOUtils.readToString(isr);
        } catch (IOException e) {
            InputStream errorStream = conn.getErrorStream();
            if (errorStream != null) {
                esr = new InputStreamReader(errorStream, StandardCharsets.UTF_8);
                IOUtils.readToString(esr);
            }
        } finally {
            if (isr != null) {
                try {
                    isr.close();
                } catch (IOException ex) {
                    // ignore
                }
            }

            if (esr != null) {
                try {
                    esr.close();
                } catch (IOException ex) {
                    // ignore
                }
            }
        }
    }
}
